1 package org.apache.maven.surefire.junitcore.pc;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.testset.TestSetFailedException;
23 import org.apache.maven.surefire.util.internal.DaemonThreadFactory;
24 import org.junit.runner.Computer;
25 import org.junit.runner.Description;
26
27 import java.util.Collection;
28 import java.util.TreeSet;
29 import java.util.concurrent.Callable;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.ScheduledExecutorService;
34 import java.util.concurrent.ThreadFactory;
35
36 import static java.util.concurrent.TimeUnit.NANOSECONDS;
37
38
39
40
41
42
43
44
45 public abstract class ParallelComputer
46 extends Computer
47 {
48 private static final ThreadFactory DAEMON_THREAD_FACTORY = DaemonThreadFactory.newDaemonThreadFactory();
49
50 private static final double NANOS_IN_A_SECOND = 1E9;
51
52 private final ShutdownStatus shutdownStatus = new ShutdownStatus();
53
54 private final ShutdownStatus forcedShutdownStatus = new ShutdownStatus();
55
56 private final long timeoutNanos;
57
58 private final long timeoutForcedNanos;
59
60 private ScheduledExecutorService shutdownScheduler;
61
62 public ParallelComputer( double timeoutInSeconds, double timeoutForcedInSeconds )
63 {
64 this.timeoutNanos = secondsToNanos( timeoutInSeconds );
65 this.timeoutForcedNanos = secondsToNanos( timeoutForcedInSeconds );
66 }
67
68 protected abstract ShutdownResult describeStopped( boolean shutdownNow );
69
70 abstract boolean shutdownThreadPoolsAwaitingKilled();
71
72 protected final void beforeRunQuietly()
73 {
74 shutdownStatus.setDescriptionsBeforeShutdown( hasTimeout() ? scheduleShutdown() : null );
75 forcedShutdownStatus.setDescriptionsBeforeShutdown( hasTimeoutForced() ? scheduleForcedShutdown() : null );
76 }
77
78 protected final boolean afterRunQuietly()
79 {
80 shutdownStatus.tryFinish();
81 forcedShutdownStatus.tryFinish();
82 boolean notInterrupted = true;
83 if ( shutdownScheduler != null )
84 {
85 shutdownScheduler.shutdownNow();
86
87
88
89
90 Thread.interrupted();
91 try
92 {
93 shutdownScheduler.awaitTermination( Long.MAX_VALUE, NANOSECONDS );
94 }
95 catch ( InterruptedException e )
96 {
97 notInterrupted = false;
98 }
99 }
100 notInterrupted &= shutdownThreadPoolsAwaitingKilled();
101 return notInterrupted;
102 }
103
104 public String describeElapsedTimeout()
105 throws TestSetFailedException
106 {
107 final StringBuilder msg = new StringBuilder();
108 final boolean isShutdownTimeout = shutdownStatus.isTimeoutElapsed();
109 final boolean isForcedShutdownTimeout = forcedShutdownStatus.isTimeoutElapsed();
110 if ( isShutdownTimeout || isForcedShutdownTimeout )
111 {
112 msg.append( "The test run has finished abruptly after timeout of " );
113 msg.append( nanosToSeconds( minTimeout( timeoutNanos, timeoutForcedNanos ) ) );
114 msg.append( " seconds.\n" );
115
116 try
117 {
118 final TreeSet<String> executedTests = new TreeSet<String>();
119 final TreeSet<String> incompleteTests = new TreeSet<String>();
120
121 if ( isShutdownTimeout )
122 {
123 printShutdownHook( executedTests, incompleteTests, shutdownStatus.getDescriptionsBeforeShutdown() );
124 }
125
126 if ( isForcedShutdownTimeout )
127 {
128 printShutdownHook( executedTests, incompleteTests,
129 forcedShutdownStatus.getDescriptionsBeforeShutdown() );
130 }
131
132 if ( !executedTests.isEmpty() )
133 {
134 msg.append( "These tests were executed in prior to the shutdown operation:\n" );
135 for ( String executedTest : executedTests )
136 {
137 msg.append( executedTest ).append( '\n' );
138 }
139 }
140
141 if ( !incompleteTests.isEmpty() )
142 {
143 msg.append( "These tests are incomplete:\n" );
144 for ( String incompleteTest : incompleteTests )
145 {
146 msg.append( incompleteTest ).append( '\n' );
147 }
148 }
149 }
150 catch ( InterruptedException e )
151 {
152 throw new TestSetFailedException( "Timed termination was interrupted.", e );
153 }
154 catch ( ExecutionException e )
155 {
156 throw new TestSetFailedException( e.getLocalizedMessage(), e.getCause() );
157 }
158 }
159 return msg.toString();
160 }
161
162 private Future<ShutdownResult> scheduleShutdown()
163 {
164 return getShutdownScheduler().schedule( createShutdownTask(), timeoutNanos, NANOSECONDS );
165 }
166
167 private Future<ShutdownResult> scheduleForcedShutdown()
168 {
169 return getShutdownScheduler().schedule( createForcedShutdownTask(), timeoutForcedNanos, NANOSECONDS );
170 }
171
172 private ScheduledExecutorService getShutdownScheduler()
173 {
174 if ( shutdownScheduler == null )
175 {
176 shutdownScheduler = Executors.newScheduledThreadPool( 2, DAEMON_THREAD_FACTORY );
177 }
178 return shutdownScheduler;
179 }
180
181 private Callable<ShutdownResult> createShutdownTask()
182 {
183 return new Callable<ShutdownResult>()
184 {
185 public ShutdownResult call()
186 throws Exception
187 {
188 boolean stampedStatusWithTimeout = ParallelComputer.this.shutdownStatus.tryTimeout();
189 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( false ) : null;
190 }
191 };
192 }
193
194 private Callable<ShutdownResult> createForcedShutdownTask()
195 {
196 return new Callable<ShutdownResult>()
197 {
198 public ShutdownResult call()
199 throws Exception
200 {
201 boolean stampedStatusWithTimeout = ParallelComputer.this.forcedShutdownStatus.tryTimeout();
202 return stampedStatusWithTimeout ? ParallelComputer.this.describeStopped( true ) : null;
203 }
204 };
205 }
206
207 private double nanosToSeconds( long nanos )
208 {
209 return (double) nanos / NANOS_IN_A_SECOND;
210 }
211
212 private boolean hasTimeout()
213 {
214 return timeoutNanos > 0;
215 }
216
217 private boolean hasTimeoutForced()
218 {
219 return timeoutForcedNanos > 0;
220 }
221
222 private static long secondsToNanos( double seconds )
223 {
224 double nanos = seconds > 0 ? seconds * NANOS_IN_A_SECOND : 0;
225 return Double.isInfinite( nanos ) || nanos >= Long.MAX_VALUE ? 0 : (long) nanos;
226 }
227
228 private static long minTimeout( long timeout1, long timeout2 )
229 {
230 if ( timeout1 == 0 )
231 {
232 return timeout2;
233 }
234 else if ( timeout2 == 0 )
235 {
236 return timeout1;
237 }
238 else
239 {
240 return Math.min( timeout1, timeout2 );
241 }
242 }
243
244 private static void printShutdownHook( Collection<String> executedTests, Collection<String> incompleteTests,
245 Future<ShutdownResult> testsBeforeShutdown )
246 throws ExecutionException, InterruptedException
247 {
248 if ( testsBeforeShutdown != null )
249 {
250 for ( final Description test : testsBeforeShutdown.get().getTriggeredTests() )
251 {
252 if ( test != null && test.getDisplayName() != null )
253 {
254 executedTests.add( test.getDisplayName() );
255 }
256 }
257
258 for ( final Description test : testsBeforeShutdown.get().getIncompleteTests() )
259 {
260 if ( test != null && test.getDisplayName() != null )
261 {
262 incompleteTests.add( test.getDisplayName() );
263 }
264 }
265 }
266 }
267 }